// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module espi_vuart_process#(
    parameter DEVICE_FAMILY          = "MAX 10 FPGA"
)(
// Avalon Memory Mapped Slave input
    input  logic                clk,                        // System clock
    input  logic                reset_n,                    // System Reset
    input  logic    [15:0]      avmm_addr,                  // AVMM address
    input  logic    [31:0]      avmm_writedata,             // AVMM Write data
    output logic    [31:0]      avmm_readdata,              // AVMM Read data
    output logic                avmm_readdatavalid,         // AVMM Read data valid
    input  logic                avmm_write,                 // Write enable
    input  logic                avmm_read,                  // Read enable
    // Interrupts
    output logic    [7:0]       vw_irq0,                    // Virtual Wire IRQ0 (eSPI Master)
    output logic    [7:0]       vw_irq1,                    // Virtual Wire IRQ1 (eSPI Master)
    output logic                espi_serial_msix_req,       // MSI-X interrupt request (BMC FW)
    input  logic                espi_serial_msix_ack,       // MSI-X interrupt acknowledge (BMC FW)
    // eSPI interface
    input  logic                channel_reset,
    input  logic                flush_vuart_fifo,
    input  logic [15:0]         io_address,
    input  logic                io_write,
    input  logic [7:0]          io_writedata,
    input  logic                io_read,
    output logic                io_waitrequest,
    output logic [7:0]          io_readdata
);
logic vuart_rbr_rd;

logic flg_vuart_thr_w;
logic flg_vuart_ier_w;
logic flg_vuart_fcr_w;
logic flg_vuart_lcr_w;
logic flg_vuart_mcr_w;
logic flg_vuart_lsr_w;
logic flg_vuart_msr_w;
logic flg_vuart_scr_w;
logic flg_vuart_dll_w;
logic flg_vuart_dlh_w;

logic dlab;

logic sio_addr_equal_002E;
logic sio_addr_equal_002F;
logic sio_data_equal_0020;
logic flg_sio_rd_req;
logic flg_sio_wr_req;
logic flg_sio_chip_id_sel;
logic sio_get_sts;

logic [15:0] vuart_avl_addr;
logic [7:0] vuart_avl_dll;
logic [7:0] vuart_avl_dlh;
logic [7:0] vuart_avl_ier;
logic [7:0] vuart_avl_mcr;
logic [7:0] vuart_avl_msr;
logic [7:0] vuart_avl_scr;
logic       vuart_rxfifo_empty;
logic vuart_addr_rbr_thr_dll;
logic vuart_addr_ier_dlh;
logic vuart_addr_iir_fcr;
logic vuart_addr_lcr;
logic vuart_addr_mcr;
logic vuart_addr_lsr;
logic vuart_addr_msr;
logic vuart_addr_scr;
logic [15:0] io_addr;
logic [7:0]  vuart_avl_lcr;
logic [7:0]  vuart_avl_lsr;
logic [7:0]  vuart_avl_iir;
logic [3:0]  interrupt_id;
logic [7:0]  vuart_rbr_rdata;
logic        vuart_rbr_get;
logic   dll_get_sts;
logic   dlh_get_sts;
logic   ier_get_sts;
logic   iir_get_sts;
logic   lcr_get_sts;
logic   mcr_get_sts;
logic   lsr_get_sts;
logic   msr_get_sts;
logic   scr_get_sts;

logic   io_read_1;
logic   io_read_2;


assign vuart_addr_rbr_thr_dll = io_addr == (vuart_avl_addr+8'h0);
assign vuart_addr_ier_dlh     = io_addr == (vuart_avl_addr+8'h1);
assign vuart_addr_iir_fcr     = io_addr == (vuart_avl_addr+8'h2);
assign vuart_addr_lcr         = io_addr == (vuart_avl_addr+8'h3);
assign vuart_addr_mcr         = io_addr == (vuart_avl_addr+8'h4);
assign vuart_addr_lsr         = io_addr == (vuart_avl_addr+8'h5);
assign vuart_addr_msr         = io_addr == (vuart_avl_addr+8'h6);
assign vuart_addr_scr         = io_addr == (vuart_avl_addr+8'h7);

assign io_addr = io_address;

assign flg_vuart_thr_w = ~dlab & flg_sio_wr_req & vuart_addr_rbr_thr_dll;
assign flg_vuart_ier_w = ~dlab & flg_sio_wr_req & vuart_addr_ier_dlh;
assign flg_vuart_fcr_w = flg_sio_wr_req & vuart_addr_iir_fcr;
assign flg_vuart_lcr_w = flg_sio_wr_req & vuart_addr_lcr;
assign flg_vuart_mcr_w = flg_sio_wr_req & vuart_addr_mcr;
assign flg_vuart_lsr_w = flg_sio_wr_req & vuart_addr_lsr;
assign flg_vuart_msr_w = flg_sio_wr_req & vuart_addr_msr;
assign flg_vuart_scr_w = flg_sio_wr_req & vuart_addr_scr;
assign flg_vuart_dll_w = dlab & flg_sio_wr_req & vuart_addr_rbr_thr_dll;
assign flg_vuart_dlh_w = dlab & flg_sio_wr_req & vuart_addr_ier_dlh;

assign dlab = vuart_avl_lcr[7];
assign vuart_rbr_get = ~dlab & io_read & ~io_read_1 & vuart_addr_rbr_thr_dll & ~vuart_rxfifo_empty;

// Vuart readback mux
always @(posedge clk) begin
    case (1'b1)
      vuart_rbr_rd: io_readdata <= vuart_rbr_rdata;
      sio_get_sts:  io_readdata <= 8'h03;
      dll_get_sts:  io_readdata <= vuart_avl_dll;
      dlh_get_sts:  io_readdata <= vuart_avl_dlh;
      ier_get_sts:  io_readdata <= vuart_avl_ier;
      iir_get_sts:  io_readdata <= vuart_avl_iir;
      lcr_get_sts:  io_readdata <= vuart_avl_lcr;
      mcr_get_sts:  io_readdata <= vuart_avl_mcr;
      lsr_get_sts:  io_readdata <= vuart_avl_lsr;
      msr_get_sts:  io_readdata <= vuart_avl_msr;
      scr_get_sts:  io_readdata <= vuart_avl_scr;
      default:      io_readdata <= 0;
    endcase
end // always @ (posedge clk)

    assign io_waitrequest = io_read & io_read_1 & ~io_read_2;

    always @(posedge clk) begin
        io_read_1 <= io_read;
        io_read_2 <= io_read_1;
    end
    
// eSPI vuart register read decodes
always @(posedge clk) begin
    vuart_rbr_rd <= ~dlab & flg_sio_rd_req & vuart_addr_rbr_thr_dll;
    sio_get_sts  <= flg_sio_rd_req & sio_addr_equal_002F & flg_sio_chip_id_sel;
    dll_get_sts  <= dlab & flg_sio_rd_req & vuart_addr_rbr_thr_dll;
    dlh_get_sts  <= dlab & flg_sio_rd_req & vuart_addr_ier_dlh;
    ier_get_sts  <= ~dlab & flg_sio_rd_req & vuart_addr_ier_dlh;
    iir_get_sts  <= flg_sio_rd_req & vuart_addr_iir_fcr;
    lcr_get_sts  <= flg_sio_rd_req & vuart_addr_lcr;
    mcr_get_sts  <= flg_sio_rd_req & vuart_addr_mcr;
    lsr_get_sts  <= flg_sio_rd_req & vuart_addr_lsr;
    msr_get_sts  <= flg_sio_rd_req & vuart_addr_msr;
    scr_get_sts  <= flg_sio_rd_req & vuart_addr_scr;
end

initial flg_sio_chip_id_sel = 0;
always @(posedge clk) begin
    if (flg_sio_wr_req && sio_addr_equal_002E) begin
        flg_sio_chip_id_sel <= sio_data_equal_0020;
    end
end

//support SIO read CHIP ID: return value is 03
assign sio_addr_equal_002E  = io_addr == 16'h2E;
assign sio_addr_equal_002F  = io_addr == 16'h2F;
assign sio_data_equal_0020  = (io_writedata == 8'h20);

assign flg_sio_wr_req       = io_write;
assign flg_sio_rd_req       = io_read;

`ifdef SIMULATION
localparam ONE_CHAR_TIME = 14'd2000;
`else
localparam ONE_CHAR_TIME = 14'd9548;
`endif

uart_16550  #(
 .ONE_CHAR_TIME          ( ONE_CHAR_TIME          ),
 .DEVICE_FAMILY          ( DEVICE_FAMILY          )
) uart_16550 (
   
     // AVMM BMC FW interface 
    .clk                ( clk                 ),
    .reset_n            ( reset_n             ),
    .channel_reset_n    ( ~channel_reset      ),
    .avmm_addr          ( avmm_addr           ),
    .avmm_writedata     ( avmm_writedata      ),
    .avmm_readdata      ( avmm_readdata       ),
    .avmm_readdatavalid ( avmm_readdatavalid  ),
    .avmm_write         ( avmm_write          ),
    .avmm_read          ( avmm_read           ),

    // interrupts: espi over vw
    .vw_irq0            ( vw_irq0             ),
    .vw_irq1            ( vw_irq1             ),
    // interrupts: MSI-X over PCIe
    .espi_serial_msix_req ( espi_serial_msix_req ),
    .espi_serial_msix_ack ( espi_serial_msix_ack ),

    // FIFO I/F signals
    .flush_vuart_fifo   ( flush_vuart_fifo    ),
    .vuart_rbr_get      ( vuart_rbr_get       ),
    .vuart_rbr_rdata    ( vuart_rbr_rdata     ),
    .vuart_rxfifo_empty ( vuart_rxfifo_empty  ),
    .iir_get_sts        ( iir_get_sts         ),
    .lsr_get_sts        ( lsr_get_sts         ),
    .msr_get_sts        ( msr_get_sts         ),

    // espi master write control
    .espi_writedata     ( io_writedata        ),
    .flg_vuart_ier_w    ( flg_vuart_ier_w     ),
    .flg_vuart_dll_w    ( flg_vuart_dll_w     ),
    .flg_vuart_dlh_w    ( flg_vuart_dlh_w     ),
    .flg_vuart_fcr_w    ( flg_vuart_fcr_w     ),
    .flg_vuart_lcr_w    ( flg_vuart_lcr_w     ),
    .flg_vuart_mcr_w    ( flg_vuart_mcr_w     ),
    .flg_vuart_scr_w    ( flg_vuart_scr_w     ),
    .flg_vuart_thr_w    ( flg_vuart_thr_w     ),

    // espi master read control
    .vuart_avl_dll      ( vuart_avl_dll       ),
    .vuart_avl_dlh      ( vuart_avl_dlh       ),
    .vuart_avl_ier      ( vuart_avl_ier       ),
    .vuart_avl_iir      ( vuart_avl_iir       ),
    .vuart_avl_lcr      ( vuart_avl_lcr       ),
    .vuart_avl_mcr      ( vuart_avl_mcr       ),
    .vuart_avl_lsr      ( vuart_avl_lsr       ),
    .vuart_avl_msr      ( vuart_avl_msr       ),
    .vuart_avl_scr      ( vuart_avl_scr       ),
    .vuart_avl_addr     ( vuart_avl_addr      )
);
 

endmodule
